#include "signaldiagram.h"
#include "appsettings.h"

SignalDiagram::SignalDiagram()
{
    this->widthPerSignal = 10;
    this->heightPerSignal = 20;
    this->spaceHeight = 10;
    this->spaceForTitles = 70;

    this->timeInterval = 10;
    this->timeTableVisible = false;
    this->timeTagsVisible = false;

    this->penColor = Qt::blue;

    this->drawFont.setFamily(AppSettings::getInstance().defaultFontFamily);
    this->drawFont.setPixelSize(10);

    this->signalCount = 0;
    this->startSignal = 0;
    this->startData = 0;
    this->dataCount = 0;
}

bool SignalDiagram::compareByIndex(SignalContainer *one, SignalContainer *two)
{
    return one->sortIndex < two->sortIndex;
}

void SignalDiagram::generateDiagram(const QVector<SignalContainer*> &data, int w, int h, int start, int beginsignal, qreal scale)
{
    qreal currentWidthPerSecond = this->widthPerSignal * scale;
    
    if(data.count() > 0)
    {
        this->BackUp = data;
        qStableSort(this->BackUp.begin(),
              this->BackUp.end(),
              SignalDiagram::compareByIndex);

        this->signalCount = w / currentWidthPerSecond;
        this->dataCount = (h - this->spaceHeight - this->heightPerSignal) / this->heightPerSignal;
        this->startSignal = start;
        this->startData = beginsignal;
    }
}

void SignalDiagram::generateDiagram(const QVector<SignalContainer *> &data, QPaintDevice *device, int start, int beginsignal, qreal scale)
{
    qreal currentWidthPerSecond = this->widthPerSignal * scale;
    
    this->generateDiagram(data, device->width(), device->height(), start, beginsignal, scale);
    if (device)
        paintImage(device, currentWidthPerSecond);
}

QImage* SignalDiagram::generateImage(qreal scale)
{
    QImage *image = NULL;
    
    qreal currentWidthPerSecond = this->widthPerSignal * scale;

    if((this->BackUp.count() > 0) &&
       (this->startSignal >= 0) &&
       (this->signalCount > 0 ))
    {
        image = new QImage(this->signalCount * currentWidthPerSecond,
                          (this->BackUp.count() + 1) *
                          (this->spaceHeight + this->heightPerSignal) + this->spaceHeight,
                          QImage::Format_ARGB32_Premultiplied);
        image->fill(0xFFFFFF);

        // Отрисовываем картинку
        this->paintImage(image, currentWidthPerSecond);
    }

    return image;
}

void SignalDiagram::paintImage(QPaintDevice* img, qreal widthpersecond)
{
    // Приращение времени
    int dtime = this->timeInterval * widthpersecond;

    // Количество меток
    int lineCount = qMin(BackUp.at(0)->getCapacity() - this->startSignal, this->signalCount) /
                    this->timeInterval + 1;
    
    // Инструменты для отрисовки
    QPainter painter;

    QPen pen;
    QPen cpen;
    QPen dpen;

    pen.setWidthF(1.5f);
    pen.setColor(Qt::black);    

    dpen.setWidthF(1.5f);
    dpen.setColor(Qt::gray);
    dpen.setStyle(Qt::DashLine);

    cpen.setWidthF(1.5f);
    cpen.setColor(this->penColor);

    // Отрисовка сетки
    if(this->timeTableVisible)
    {
        painter.begin(img);
        painter.setPen(dpen);        

        for(int i = 0; i <= lineCount; i++)
        {
            int n = qMin(this->startData + this->dataCount, BackUp.count()) - this->startData;
            painter.drawLine(i * dtime + this->spaceForTitles,
                             0,
                             i * dtime + this->spaceForTitles,
                             (this->spaceHeight + this->heightPerSignal)* n + this->heightPerSignal);
        }
        painter.end();
    }

    // Отрисовка графика
    painter.begin(img);    
    painter.setFont(this->drawFont);

    int x = this->spaceForTitles;
    int y = this->spaceHeight;

    for (int si = this->startData;
        (si < this->startData + this->dataCount) && (si < BackUp.count());
        si++)
    {
        SignalContainer* sigc = BackUp.at(si);

        // Отрисовываем метки

        painter.setPen(pen);

        QRectF rect(0,y,this->spaceForTitles,this->heightPerSignal);
        painter.drawText(rect, sigc->label, QTextOption());

        signed char first = sigc->at(this->startSignal);
        int ly;

        if (first < 0)
            ly = this->heightPerSignal / 2;
        else
            ly = (1 - first) * this->heightPerSignal;

        // Отисовка одной полосы сигнала

        painter.setPen(cpen);

        for (int i = this->startSignal;
             (i < sigc->getCapacity()) && (i < this->startSignal + this->signalCount);
             i++)
        {
            if(sigc->at(i) == first)
            {
                painter.drawLine(x, y + ly, x + widthpersecond, y + ly);
            }
            else
            {
                int ny = 0;
                if(sigc->at(i) >= 0)
                    ny = (1 - sigc->at(i)) * this->heightPerSignal;
                else
                    ny = this->heightPerSignal / 2;

                painter.drawLine(x, y + ly, x, y + ny);
                ly = ny;
                painter.drawLine(x, y + ly, x + widthpersecond, y + ly);
            }

            first = sigc->at(i);
            x += widthpersecond;
        }

        y += this->spaceHeight + this->heightPerSignal;
        x = this->spaceForTitles;
    }
    
    painter.end();

    // Отрисовка меток времени
    if(this->timeTagsVisible)
    {
        painter.begin(img);
        painter.setPen(pen);
        painter.setFont(this->drawFont);

        for(int i = 0; i <= lineCount; i++)
        {
            QString time;
            time.setNum(this->startSignal + i*this->timeInterval);
            int fx = i * dtime + this->spaceForTitles;
            int fy = y;

            painter.drawLine(fx, y, fx, y + this->heightPerSignal);

            //магия чисел :]
            painter.drawText(fx + this->heightPerSignal / 4, fy + this->heightPerSignal / 2, time);
        }
        
        painter.end();
    }    
}
